/**
 * @author مۇختەرجان مەخمۇت (mutearjan mahmood)
 * @email yeganaaa@163.com`
 * @create date 2020-09-05 18:03:18
 * @modify date 2020-09-05 18:03:18
 * @desc [description]
*/

import { RpcClient, RpcServer, State, StateManagerInMemory, KeepAliveRpcServerHost } from "dream_rpc";
import { WebSocketServerHost } from "dream_rpc_node_websocket"
import { IBroadcastClientInterface, IBroadcastServerInterface } from "./Shared";
import * as Http from "http"
import WebSocket from "ws";
import { TcpSocketServerHost } from "dream_rpc_node_tcp_socket"
import * as Net from "net"

/**
 * State object of rpc client on server
 */
class MyBroadcastClientState extends State {}
/**
 * Server side rpc methods implementation
 */
class MyBroadcastServerImplementation implements IBroadcastServerInterface {
    /**Non rpc method object names are should be start with "_", for example: "_client", "_xxNotRpcObject", for more details: please visit github (or gitee) documentation.*/
    public constructor(private _client: MyBroadcastClient) {}
    public async sendBroadcast(args: { message: string; sender: string; }): Promise<void> {
        console.log(`A broadcast message received by [${args.sender}] that content of [${args.message}]`)
        /**get all connected clients on this server and send the received message to each */
        this._client.server.clients.forEach((client) => {
            (client as MyBroadcastClient).bridge.remoteInterface.onBroadcastReceived({
                message: args.message,
                sender: args.sender
            })
        })
    }
}
class MyBroadcastClient extends RpcClient<IBroadcastServerInterface, IBroadcastClientInterface, MyBroadcastClientState> {
    protected async createState(): Promise<MyBroadcastClientState> {
        return new MyBroadcastClientState()
    }
    protected async getLocalInterfaceImplementation(): Promise<IBroadcastServerInterface> {
        return new MyBroadcastServerImplementation(this)
    }
    /**
     * This is optional lifecycle method, don't write if you don't need
     */
    protected async onConnected(): Promise<void> {
        await super.onConnected() //Must call super
        console.log("Client connected")
    }
    /**
     * This is optional lifecycle method, don't write if you don't need
     */
    protected async onReady(): Promise<void> {
        await super.onReady() //Must call super
        console.log("Connected client is now ready to use")

        //Dispatch a broadcast with "MyBroadcastServerImplementation" instance.
        this.bridge.localInterface.sendBroadcast({
            sender: "New Guy",
            message: "Hello everyone, I'm a new guy"
        })
    }
    /**
     * This is optional lifecycle method, don't write if you don't need
     */
    protected async onDisconnected(): Promise<void> {
        await super.onDisconnected() //Must call super
        console.log("Client is disconnected")
    }
}
class MyBroadcastServer extends RpcServer<IBroadcastServerInterface, IBroadcastClientInterface, MyBroadcastClientState> {
    protected async createClientInstance(): Promise<RpcClient<IBroadcastServerInterface, IBroadcastClientInterface, MyBroadcastClientState>> {
        return new MyBroadcastClient()
    }
    /**
     * This is optional lifecycle method, don't write if you don't need
     */
    protected async onLaunch(): Promise<void> {
        await super.onLaunch() //Must call super
        console.log("Rpc server start: [OK]")
    }
    protected async onShutdown(): Promise<void> {
        await super.onShutdown() //Must call super
        console.log("Rpc server is shutdown")
    }
}

async function main() {
    const httpServer = Http.createServer((request, response) => {
        response.end("Please communicate with WebSocket protocol")
    })
    httpServer.listen(8000)

    const myServer = new MyBroadcastServer(
        new KeepAliveRpcServerHost(
            new WebSocketServerHost(async () => new WebSocket.Server({
                path: "/Broadcast",
                server: httpServer
            })),
            // new TcpSocketServerHost(async () => Net.createServer(), async server => {
            //     server.listen(8001)
            // }),
            10 * 1000
        ),
        new StateManagerInMemory()
    )

    await myServer.prepareForLaunch() //Prepare for launch server, in this step, server is register internal callback for launch
    await myServer.launch() //start rpc server
}
main()
